﻿using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using IInject.Internal;

namespace IInject.Registration.Scanning
{
	public class DefaultAssemblyScanner : ContainerMember, IAssemblyScanner
	{
		#region Fields

		private readonly List<IScanningRule> _conventions = new List<IScanningRule>();

		#endregion

		#region Constructors

		public DefaultAssemblyScanner(IContainer container) : base(container)
		{
		}

		#endregion

		#region Methods

		public virtual void Scan(Assembly assembly)
		{
			if (_conventions.Count == 0)
			{
				_conventions.AddRange(ScanRules.Current.ToArray());
			}

			var types = assembly.GetExportedTypes();
			var context = new ScanningContext(Container, _conventions.ToArray());

			for (int i = 0; i < types.Length; i++)
			{
				context.AddIfMatched(types[i]);
			}

			var items = context.GetPendingItems();

			for (int i = 0; i < items.Length; i++)
			{
				Container.Map(a =>
				{
					a.From(items[i].From);

					if (items[i].HasToType)
					{
						a.To(items[i].To);
					}
				});
			}
		}

		public void Scan(string assemblyName)
		{
			Scan(Assembly.LoadFrom(assemblyName));
		}

		public void Scan(AssemblyName assemblyName)
		{
			Scan(Assembly.LoadFrom(assemblyName.FullName));
		}

		public void Add(IScanningRule convention)
		{
			_conventions.Add(convention);
		}

		public void Using(params IScanningRule[] conventions)
		{
			_conventions.Clear();
			_conventions.AddRange(conventions);
		}

		public void ScanCallingAssembly()
		{
			Scan(Assembly.GetCallingAssembly());
		}

		public void ScanExecutingAssembly()
		{
			Scan(Assembly.GetExecutingAssembly());
		}

		public void FromEntryAssembly()
		{
			Scan(Assembly.GetEntryAssembly());
		}

		#endregion
	}
}
